package ch.bfh.fbi.mobicomp.zap08;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;
import ch.quantasy.android.speech.SpeechService;
import ch.quantasy.android.speech.string.FrenchString;
import ch.quantasy.android.speech.string.GermanString;
import ch.quantasy.android.speech.string.UKString;

/**
 * Zap07 does work fine, even if the display is switched off. However, it keeps
 * the device awake all the time. That will drain the battery during sleep Due
 * to the wake-lock.
 * 
 * Zap08 uses the alarmManager, and requests to be notified. This way, the
 * device can go to deep-sleep during the waiting period. The alarmManager will
 * wake up the device and will notify the service (Broadcastreceiver)
 * 
 * @author quantasy
 * 
 */
public class Zap08Service extends Service {

	private static final String TAG = "ZAP08";

	private PowerManager.WakeLock wakeLock;
	private AlarmManager am;
	private PendingIntent sender;
	public static final String ACTION_REPEAT = "ch.bfh.fbi.mobicomp.zap08.ACTION_REPEAT";
	private IntentFilter alarmRepeater;
	private AlarmHandler alarmHandler;

	public static final String REMOTE_ZAPPER_ACTION = "ch.bfh.fbi.mobicomp.zap08.Zap08Service.REMOTE_ZAPPER_ACTION";
	public static final String ZAPPER_KEY_WORD = "ZAP:";
	private static final String destructionKey = "destroyMe";
	private static final String haltKey = "stopDestroyMe";

	private final Intent speechIntent;
	private long countdown;

	public static Intent getZapperIntent(final String message) {
		Intent intent = null;
		if (message.startsWith(Zap08Service.ZAPPER_KEY_WORD)) {
			intent = new Intent(Zap08Service.REMOTE_ZAPPER_ACTION);
			intent.putExtra("message",
					message.substring(Zap08Service.ZAPPER_KEY_WORD.length()));
		}
		return intent;
	}

	public Zap08Service() {
		this.speechIntent = SpeechService.getSpeechIntent();
	}

	@Override
	public IBinder onBind(final Intent intent) {
		// We do not want to grand explicite access!
		return null;
	}

	@Override
	public int onStartCommand(final Intent intent, final int flags,
			final int startId) {
		Log.d(Zap08Service.TAG, "Service Intent received: " + intent);
		if (intent == null) {
			return super.onStartCommand(intent, flags, startId);
		}
		if (Zap08Service.destructionKey.equals(intent.getExtras().getString(
				"message"))) {
			if (this.am == null) {
				Log.d(Zap08Service.TAG, "destruction initiated...");
				this.initiateAutoDestruction();
			}
			Log.d(Zap08Service.TAG, "Start");
		} else if (Zap08Service.haltKey.equals(intent.getExtras().getString(
				"message"))) {
			Log.d(Zap08Service.TAG, "destruction aborted...");
			this.resetAutoDestruction();
			Log.d("Zapper", "stop");
		}

		else {
			Log.d(Zap08Service.TAG, "unknown command.");
		}

		return super.onStartCommand(intent, flags, startId);
	}

	private void resetAutoDestruction() {
		if (this.countdown == 0) {
			return;
		}
		this.initiateWakeLock();
		this.countdown = 0;
		this.unregisterAlarmManager();
		SpeechService.setPhrase(this.speechIntent, new UKString(
				"Self destruction sequence aborted."), new GermanString(
				"Selbstzerstörungssequenz abgebrochen."), new FrenchString(
				"Séquence d'autodestruction avortée."));
		this.startService(this.speechIntent);
		this.removeWakeLock();
	}

	private void initiateAutoDestruction() {
		if (this.countdown != 0) {
			return;
		}
		this.initiateWakeLock();
		this.registerAlarmManager();
		this.countdown = System.currentTimeMillis() + (3 * 60 * 1000);
		SpeechService.setPhrase(this.speechIntent, new UKString(
				"Self destruction sequence initiated."), new GermanString(
				"Selbstzerstörungs Sequenz eingeleitet."), new FrenchString(
				"Séquence d'autodestruction declanchée."));
		this.startService(this.speechIntent);
		this.countDown();
		this.removeWakeLock();
	}

	private void countDown() {
		this.initiateWakeLock();
		final int timeLeft = (int) ((this.countdown - System
				.currentTimeMillis()) / 1000);
		if (timeLeft > 70) {
			this.setAlarmManagerRepeating(60000);
			this.minutesCount(timeLeft);
		} else if (timeLeft > 11) {
			this.setAlarmManagerRepeating(10000);
			this.secondsBlockCount(timeLeft);
		} else if (timeLeft > 0) {
			this.setAlarmManagerRepeating(1000);
			this.secondsCount(timeLeft);
		} else {
			this.destroy();
		}
		this.removeWakeLock();
	}

	private void minutesCount(final int timeLeft) {
		SpeechService.setPhrase(this.speechIntent, new UKString(""
				+ (1 + (timeLeft / (60))) + "Minutes until self destruction"),
				new GermanString("" + (1 + (timeLeft / (60)))
						+ "Minuten bis zur Selbstzerstörung"),
				new FrenchString("" + (1 + (timeLeft / (60)))
						+ "minutes jusqu'à ce que l'auto-destruction"));
		this.startService(this.speechIntent);
	}

	private void secondsBlockCount(final int timeLeft) {
		if ((timeLeft % 3) == 0) {
			SpeechService.setPhrase(this.speechIntent, new UKString(""
					+ (10 + ((timeLeft / 10) * 10))
					+ "Seconds until self destruction"));
		} else if ((timeLeft % 3) == 1) {
			SpeechService.setPhrase(this.speechIntent, new GermanString(""
					+ (10 + ((timeLeft / 10) * 10))
					+ "Sekunden bis zur Selbstzerstörung"));
		} else {
			SpeechService.setPhrase(this.speechIntent, new FrenchString(""
					+ (10 + ((timeLeft / 10) * 10))
					+ "seconds jusqu'à ce que l'auto-destruction"));
		}
		this.startService(this.speechIntent);
	}

	private void secondsCount(final int timeLeft) {
		SpeechService.setPhrase(this.speechIntent,
				new UKString("" + (timeLeft)));
		this.startService(this.speechIntent);
	}

	private void destroy() {
		this.countdown = 0;
		this.unregisterAlarmManager();
		SpeechService.setPhrase(this.speechIntent, new UKString(
				"Self destroying now."));
		this.startService(this.speechIntent);
	}

	private void initiateWakeLock() {
		if (this.wakeLock == null) {
			final PowerManager pm = (PowerManager) this
					.getSystemService(Context.POWER_SERVICE);
			this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
					| PowerManager.ON_AFTER_RELEASE, Zap08Service.TAG);
			this.wakeLock.acquire();
		}

	}

	private void removeWakeLock() {
		if (this.wakeLock != null) {
			this.wakeLock.release();
			this.wakeLock = null;
		}
	}

	public void registerAlarmManager() {
		this.alarmHandler = new AlarmHandler();
		this.am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
		if (this.alarmRepeater == null) {
			this.alarmRepeater = new IntentFilter(Zap08Service.ACTION_REPEAT);
			this.registerReceiver(this.alarmHandler, this.alarmRepeater);
		}
		final Intent intent = new Intent(Zap08Service.ACTION_REPEAT);

		// In reality, you would want to have a static variable for the
		// request code instead of 0
		this.sender = PendingIntent.getBroadcast(this, 0, intent,
				PendingIntent.FLAG_UPDATE_CURRENT);
		Log.d(Zap08Service.TAG, "Registered at the AlarmManager");
	}

	public void setAlarmManagerRepeating(final long delayInMillis) {
		if (this.am == null) {
			return;
		}

		this.am.setRepeating(AlarmManager.RTC_WAKEUP,
				System.currentTimeMillis() + delayInMillis, delayInMillis,
				this.sender);
		Log.d(Zap08Service.TAG, "Set the next Alert");
	}

	public void unregisterAlarmManager() {
		if (this.am == null) {
			return;
		}
		Log.d(Zap08Service.TAG, "Unregistering at the AlarmManager");
		this.am.cancel(this.sender);
		this.unregisterReceiver(this.alarmHandler);
		this.sender = null;
		this.alarmHandler = null;
		this.alarmRepeater = null;
		this.am = null;
	}

	class AlarmHandler extends BroadcastReceiver {
		@Override
		public void onReceive(final Context context, final Intent intent) {
			Log.d(Zap08Service.TAG, "Triggered by AlarmManager");
			Zap08Service.this.countDown();
		}

	}
}
